/*

* EASYDROPDOWN - A Drop-down Builder for Styleable Inputs and Menus

* Version: 2.1.4

* License: Creative Commons Attribution 3.0 Unported - CC BY 3.0

* http://creativecommons.org/licenses/by/3.0/

* This software may be used freely on commercial and non-commercial projects with attribution to the author/copyright holder.

* Author: Patrick Kunka

* Copyright 2013 Patrick Kunka, All Rights Reserved

*/





(function($){

	

	function EasyDropDown(){

		this.isField = true,

		this.down = false,

		this.inFocus = false,

		this.disabled = false,

		this.cutOff = false,

		this.hasLabel = false,

		this.keyboardMode = false,

		this.nativeTouch = true,

		this.wrapperClass = 'dropdown',

		this.onChange = null;

	};

	

	EasyDropDown.prototype = {

		constructor: EasyDropDown,

		instances: {},

		init: function(domNode, settings){

			var	self = this;

			

			$.extend(self, settings);

			self.$select = $(domNode);

			self.id = domNode.id;

			self.options = [];

			self.$options = self.$select.find('option');

			self.isTouch = 'ontouchend' in document;

			self.$select.removeClass(self.wrapperClass+' dropdown');

			if(self.$select.is(':disabled')){

				self.disabled = true;

			};

			if(self.$options.length){

				self.$options.each(function(i){

					var $option = $(this);

					if($option.is(':selected')){

						self.selected = {

							index: i,

							title: $option.text()

						}

						self.focusIndex = i;

					};

					if($option.hasClass('label') && i == 0){

						self.hasLabel = true;

						self.label = $option.text();

						$option.attr('value','');

					} else {

						self.options.push({

							domNode: $option[0],

							title: $option.text(),

							value: $option.val(),

							selected: $option.is(':selected')

						});

					};

				});

				if(!self.selected){

					self.selected = {

						index: 0,

						title: self.$options.eq(0).text()

					}

					self.focusIndex = 0;

				};

				self.render();

			};

		},

	

		render: function(){

			var	self = this,

				touchClass = self.isTouch && self.nativeTouch ? ' touch' : '',

				disabledClass = self.disabled ? ' disabled' : '';

			

			self.$container = self.$select.wrap('<div class="'+self.wrapperClass+touchClass+disabledClass+'"><span class="old"/></div>').parent().parent();

			self.$active = $('<span class="selected">'+self.selected.title+'</span>').appendTo(self.$container);

			self.$carat = $('<span class="carat"/>').appendTo(self.$container);

			self.$scrollWrapper = $('<div><ul/></div>').appendTo(self.$container);

			self.$dropDown = self.$scrollWrapper.find('ul');

			self.$form = self.$container.closest('form');

			$.each(self.options, function(){

				var	option = this,

					active = option.selected ? ' class="active"':'';

				self.$dropDown.append('<li'+active+'>'+option.title+'</li>');

			});

			self.$items = self.$dropDown.find('li');

			

			if(self.cutOff && self.$items.length > self.cutOff)self.$container.addClass('scrollable');

			

			self.getMaxHeight();

	

			if(self.isTouch && self.nativeTouch){

				self.bindTouchHandlers();

			} else {

				self.bindHandlers();

			};

		},

		

		getMaxHeight: function(){

			var self = this;

			

			self.maxHeight = 0;

			

			for(i = 0; i < self.$items.length; i++){

				var $item = self.$items.eq(i);

				self.maxHeight += $item.outerHeight();

				if(self.cutOff == i+1){

					break;

				};

			};

		},

		

		bindTouchHandlers: function(){

			var	self = this;

			self.$container.on('click.easyDropDown',function(){

				self.$select.focus();

			});

			self.$select.on({

				change: function(){

					var	$selected = $(this).find('option:selected'),

						title = $selected.text(),

						value = $selected.val();

						

					self.$active.text(title);

					if(typeof self.onChange === 'function'){

						self.onChange.call(self.$select[0],{

							title: title, 

							value: value

						});

					};

				},

				focus: function(){

					self.$container.addClass('focus');

				},

				blur: function(){

					self.$container.removeClass('focus');

				}

			});

		},

	

		bindHandlers: function(){

			var	self = this;

			self.query = '';

			self.$container.on({

				'click.easyDropDown': function(){

					if(!self.down && !self.disabled){

						self.open();

					} else {

						self.close();

					};

				},

				'mousemove.easyDropDown': function(){

					if(self.keyboardMode){

						self.keyboardMode = false;

					};

				}

			});

			

			$('body').on('click.easyDropDown.'+self.id,function(e){

				var $target = $(e.target),

					classNames = self.wrapperClass.split(' ').join('.');



				if(!$target.closest('.'+classNames).length && self.down){

					self.close();

				};

			});



			self.$items.on({

				'click.easyDropDown': function(){

					var index = $(this).index();

					self.select(index);

					self.$select.focus();

				},

				'mouseover.easyDropDown': function(){

					if(!self.keyboardMode){

						var $t = $(this);

						$t.addClass('focus').siblings().removeClass('focus');

						self.focusIndex = $t.index();

					};

				},

				'mouseout.easyDropDown': function(){

					if(!self.keyboardMode){

						$(this).removeClass('focus');

					};

				}

			});



			self.$select.on({

				'focus.easyDropDown': function(){

					self.$container.addClass('focus');

					self.inFocus = true;

				},

				'blur.easyDropDown': function(){

					self.$container.removeClass('focus');

					self.inFocus = false;

				},

				'keydown.easyDropDown': function(e){

					if(self.inFocus){

						self.keyboardMode = true;

						var key = e.keyCode;



						if(key == 38 || key == 40 || key == 32){

							e.preventDefault();

							if(key == 38){

								self.focusIndex--

								self.focusIndex = self.focusIndex < 0 ? self.$items.length - 1 : self.focusIndex;

							} else if(key == 40){

								self.focusIndex++

								self.focusIndex = self.focusIndex > self.$items.length - 1 ? 0 : self.focusIndex;

							};

							if(!self.down){

								self.open();

							};

							self.$items.removeClass('focus').eq(self.focusIndex).addClass('focus');

							if(self.cutOff){

								self.scrollToView();

							};

							self.query = '';

						};

						if(self.down){

							if(key == 9 || key == 27){

								self.close();

							} else if(key == 13){

								e.preventDefault();

								self.select(self.focusIndex);

								self.close();

								return false;

							} else if(key == 8){

								e.preventDefault();

								self.query = self.query.slice(0,-1);

								self.search();

								clearTimeout(self.resetQuery);

								return false;

							} else if(key != 38 && key != 40){

								var letter = String.fromCharCode(key);

								self.query += letter;

								self.search();

								clearTimeout(self.resetQuery);

							};

						};

					};

				},

				'keyup.easyDropDown': function(){

					self.resetQuery = setTimeout(function(){

						self.query = '';

					},1200);

				}

			});

			

			self.$dropDown.on('scroll.easyDropDown',function(e){

				if(self.$dropDown[0].scrollTop >= self.$dropDown[0].scrollHeight - self.maxHeight){

					self.$container.addClass('bottom');

				} else {

					self.$container.removeClass('bottom');

				};

			});

			

			if(self.$form.length){

				self.$form.on('reset.easyDropDown', function(){

					var active = self.hasLabel ? self.label : self.options[0].title;

					self.$active.text(active);

				});

			};

		},

		

		unbindHandlers: function(){

			var self = this;

			

			self.$container

				.add(self.$select)

				.add(self.$items)

				.add(self.$form)

				.add(self.$dropDown)

				.off('.easyDropDown');

			$('body').off('.'+self.id);

		},

		

		open: function(){

			var self = this,

				scrollTop = window.scrollY || document.documentElement.scrollTop,

				scrollLeft = window.scrollX || document.documentElement.scrollLeft,

				scrollOffset = self.notInViewport(scrollTop);



			self.closeAll();

			self.getMaxHeight();

			self.$select.focus();

			window.scrollTo(scrollLeft, scrollTop+scrollOffset);

			self.$container.addClass('open');

			self.$scrollWrapper.css('height',self.maxHeight+'px');

			self.down = true;

		},

		

		close: function(){

			var self = this;

			self.$container.removeClass('open');

			self.$scrollWrapper.css('height','0px');

			self.focusIndex = self.selected.index;

			self.query = '';

			self.down = false;

		},

		

		closeAll: function(){

			var self = this,

				instances = Object.getPrototypeOf(self).instances;

			for(var key in instances){

				var instance = instances[key];

				instance.close();

			};

		},

	

		select: function(index){

			var self = this;

			

			if(typeof index === 'string'){

				index = self.$select.find('option[value='+index+']').index() - 1;

			};

			

			var	option = self.options[index],

				selectIndex = self.hasLabel ? index + 1 : index;

			self.$items.removeClass('active').eq(index).addClass('active');

			self.$active.text(option.title);

			self.$select

				.find('option')

				.removeAttr('selected')

				.eq(selectIndex)

				.prop('selected',true)

				.parent()

				.trigger('change');

				

			self.selected = {

				index: index,

				title: option.title

			};

			self.focusIndex = i;

			if(typeof self.onChange === 'function'){

				self.onChange.call(self.$select[0],{

					title: option.title, 

					value: option.value

				});

			};

		},

		

		search: function(){

			var self = this,

				lock = function(i){

					self.focusIndex = i;

					self.$items.removeClass('focus').eq(self.focusIndex).addClass('focus');

					self.scrollToView();	

				},

				getTitle = function(i){

					return self.options[i].title.toUpperCase();

				};

				

			for(i = 0; i < self.options.length; i++){

				var title = getTitle(i);

				if(title.indexOf(self.query) == 0){

					lock(i);

					return;

				};

			};

			

			for(i = 0; i < self.options.length; i++){

				var title = getTitle(i);

				if(title.indexOf(self.query) > -1){

					lock(i);

					break;

				};

			};

		},

		

		scrollToView: function(){

			var self = this;

			if(self.focusIndex >= self.cutOff){

				var $focusItem = self.$items.eq(self.focusIndex),

					scroll = ($focusItem.outerHeight() * (self.focusIndex + 1)) - self.maxHeight;

			

				self.$dropDown.scrollTop(scroll);

			};

		},

		

		notInViewport: function(scrollTop){

			var self = this,

				range = {

					min: scrollTop,

					max: scrollTop + (window.innerHeight || document.documentElement.clientHeight)

				},

				menuBottom = self.$dropDown.offset().top + self.maxHeight;

				

			if(menuBottom >= range.min && menuBottom <= range.max){

				return 0;

			} else {

				return (menuBottom - range.max) + 5;

			};

		},

		

		destroy: function(){

			var self = this;

			self.unbindHandlers();

			self.$select.unwrap().siblings().remove();

			self.$select.unwrap();

			delete Object.getPrototypeOf(self).instances[self.$select[0].id];

		},

		

		disable: function(){

			var self = this;

			self.disabled = true;

			self.$container.addClass('disabled');

			self.$select.attr('disabled',true);

			if(!self.down)self.close();

		},

		

		enable: function(){

			var self = this;

			self.disabled = false;

			self.$container.removeClass('disabled');

			self.$select.attr('disabled',false);

		}

	};

	

	var instantiate = function(domNode, settings){

			domNode.id = !domNode.id ? 'EasyDropDown'+rand() : domNode.id;

			var instance = new EasyDropDown();

			if(!instance.instances[domNode.id]){

				instance.instances[domNode.id] = instance;

				instance.init(domNode, settings);

			};

		},

		rand = function(){

			return ('00000'+(Math.random()*16777216<<0).toString(16)).substr(-6).toUpperCase();

		};

	

	$.fn.easyDropDown = function(){

		var args = arguments,

			dataReturn = [],

			eachReturn;

			

		eachReturn = this.each(function(){

			if(args && typeof args[0] === 'string'){

				var data = EasyDropDown.prototype.instances[this.id][args[0]](args[1], args[2]);

				if(data)dataReturn.push(data);

			} else {

				instantiate(this, args[0]);

			};

		});

		

		if(dataReturn.length){

			return dataReturn.length > 1 ? dataReturn : dataReturn[0];

		} else {

			return eachReturn;

		};

	};

	

	$(function(){

		if(typeof Object.getPrototypeOf !== 'function'){

			if(typeof 'test'.__proto__ === 'object'){

				Object.getPrototypeOf = function(object){

					return object.__proto__;

				};

			} else {

				Object.getPrototypeOf = function(object){

					return object.constructor.prototype;

				};

			};

		};

		

		$('select.dropdown').each(function(){

			var json = $(this).attr('data-settings');

				settings = json ? $.parseJSON(json) : {}; 

			instantiate(this, settings);

		});

	});

})(jQuery);